<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        @import url(https://fonts.googleapis.com/css?family=Open+Sans:600);
        body {
            background-color: #dbe6e6;
            font-family: "Open Sans";
        }
        
        #world {
            position: absolute;
            width: 100%;
            height: 100%;
            overflow: hidden;
        }
        
        .nav {
            position: absolute;
            width: 400px;
            left: 50%;
            bottom: 50px;
            transform: translate(-50%, -50%);
            font-size: 12px;
            text-transform: uppercase;
            letter-spacing: 1px;
            user-select: none;
        }
        
        .previous {
            position: absolute;
            left: calc(50% - 180px);
            width: 120px;
            margin-right: 20px;
            border: 2px solid #5b9696;
            padding: 6px;
            border-radius: 4px;
        }
        
        .next {
            position: absolute;
            left: calc(50% + 20px);
            width: 90px;
            margin-left: 20px;
            border: 2px solid #5b9696;
            padding: 6px;
            border-radius: 4px;
        }
        
        .marker {
            position: absolute;
            left: 50%;
            transform: translate(-50%, 0%);
            font-size: 18px;
            padding: 4px;
        }
        
        .step {}
        
        a {
            color: #5b9696;
            text-decoration: none;
        }
        
        a:hover {
            color: #5b6060;
        }
        
        .trigoText {
            font-size: 9px;
            font-weight: bold;
            text-transform: uppercase;
        }
        
        #trigCycle {
            position: absolute;
            left: 20px;
            top: 20px;
        }
        
        .expl {
            position: absolute;
            color: #5b9696;
            width: 600px;
            left: 20px;
            top: 150px;
            line-height: 1.5;
            font-size: 12px;
            user-select: none;
        }
        
        .angle {
            color: #F00;
        }
        
        .formula {
            color: #401A07
        }
    </style>
</head>

<body>
    <div id="world" />
    <div class="expl">legRight.rotation.x = Math.cos(angle)*100;</div>
    <div class="nav">
        <div class="previous link step"><a href="#">← previous step</a>
        </div>
        <div class="marker step">0/10</div>
        <div class="next link step"><a href="#">next step →</a>
        </div>
    </div>

    <svg id="trigCycle" width="160" height="160" xmlns="https://www.w3.org/2000/svg">
        <circle cx="60" cy="60" r="50" stroke="#CCC" stroke-width="1" fill="none" />
        <path id="trigoArc" fill="#F00" opacity=".2" />
        <line id="axisCos" x1="0" y1="60" x2="120" y2="60" stroke-width="1" stroke="#CCC" />
        <line id="axisSin" x1="60" y1="0" x2="60" y2="120" stroke-width="1" stroke="#CCC" />
        <line id="trigoLine" x1="60" y1="60" x2="120" y2="60" stroke-width="1" stroke="#f00" />
        <circle id="trigoPoint" cx="0" cy="0" r="5" fill="#F00" />
        <circle id="cosPoint" cx="0" cy="60" r="2" fill="#0A0" />
        <circle id="sinPoint" cx="60" cy="0" r="2" fill="#00F" />
        <line id="cosLine" x1="60" y1="60" x2="110" y2="60" stroke-width="1" stroke="#0A0" />
        <line id="sinLine" x1="60" y1="60" x2="60" y2="110" stroke-width="1" stroke="#00f" />
        <line id="projSinLine" x1="60" y1="60" x2="110" y2="60" stroke-width="1" stroke="#CCC" stroke-dasharray="1, 2" />
        <line id="projCosLine" x1="60" y1="60" x2="60" y2="110" stroke-width="1" stroke="#CCC" stroke-dasharray="1, 2" />
        <text id="cosLabel" class="trigoText" x="122" y="62" fill="#0A0">cos</text>
        <text id="sinLabel" class="trigoText" x="62" y="120" fill="#00F">sin</text>
    </svg>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r80/three.js"></script>
    <script src="js/OBJLoader86r.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.5/dat.gui.min.js"></script>

    <script>
        //THREEJS RELATED VARIABLES 

        var scene,
            camera, fieldOfView, aspectRatio, nearPlane, farPlane,
            gobalLight, shadowLight, backLight,
            renderer,
            container,
            controls;

        var prevBtn, nextBtn;
        var expl = document.getElementsByClassName("expl")[0];
        var step = 0;
        var steps = 10;


        function initNav() {
            prevBtn = document.getElementsByClassName("previous")[0];
            nextBtn = document.getElementsByClassName("next")[0];
            stepMarker = document.getElementsByClassName("marker")[0];
            prevBtn.addEventListener("mousedown", previousStep);
            nextBtn.addEventListener("mousedown", nextStep);
            updateStep();
        }

        function nextStep() {
            step++;
            step = step % steps;
            updateStep();
        }

        function previousStep() {
            step--;
            if (step < 0) step += steps;
            updateStep();
        }

        function resetHeroMaterials() {
            hero.legR.material = brownMat;
            hero.legL.material = brownMat;
            hero.handR.material = brownMat;
            hero.handL.material = brownMat;
            hero.torso.material = blueMat;
            hero.head.material = blueMat;
        }

        function updateStep() {
            stepMarker.innerHTML = (step + 1) + "/" + steps;

            resetHeroMaterials();

            if (step == 0) {
                expl.innerHTML = "Breakdown of a walking cycle";

            } else if (step == 1) {

                expl.innerHTML = "Move the right leg : <br/><span class='formula'>legRight.position.x = cos(<span class='angle'>t</span>) * 4 <br/> legRight.position.y = sin(<span class='angle'>t</span>) * 4</span>";

                hero.legR.material = currentMaterial;

            } else if (step == 2) {


                expl.innerHTML = "Do the same with the left leg,<br/> just add PI to the angle : <br/><span class='formula'>legLeft.position.x = cos(<span class='angle'>t + PI</span>) * 4 <br/> legLeft.position.y = sin(<span class='angle'>t + PI</span>) * 4</span>";
                hero.legL.material = currentMaterial;



            } else if (step == 3) {

                expl.innerHTML = "Constrain the vertical movement<br/>of the legs to simulate the floor :<br/><span class='formula'> legRight.position.y = max(0, legRight.position.y) <br/> legLeft.position.y = max(0, legLeft.position.y)</span>";

                hero.legR.material = currentMaterial;
                hero.legL.material = currentMaterial;

            } else if (step == 4) {
                expl.innerHTML = "Move the torso and the head vertically,<br/>notice how we doubled the frequency<br/>to follow the impulse of both feets :<br/> " +
                    "<span class='formula'>torso.position.y = 8 - cos (<span class='angle'>t*2</span>) * 0.8 <br/> " +
                    "head.position.y = 21 - cos (<span class='angle'>t*2</span>) * 1.2</span>";

                hero.torso.material = currentMaterial;
                hero.head.material = currentMaterial;

            } else if (step == 5) {
                expl.innerHTML = "Rotate the torso on the Y axis :<br/> " +
                    "<span class='formula'>torso.rotation.y = - cos (<span class='angle'>t + PI</span>) * 0.2 </span> ";
                hero.torso.material = currentMaterial;

            } else if (step == 6) {

                expl.innerHTML = "Move and rotate the hands :<br/> " +
                    "<span class='formula'>handRight.position.x = - cos (<span class='angle'>t</span>) * 4 <br/> " +
                    "handRight.rotation.z = - cos (<span class='angle'>t</span>) * PI/8 <br/> " +
                    "handLeft.position.x = - cos (<span class='angle'>t + PI</span>) * 4 <br/> " +
                    "handLeft.rotation.z = - cos (<span class='angle'>t + PI</span>) * PI/8 </span>";

                hero.handR.material = currentMaterial;
                hero.handL.material = currentMaterial;

            } else if (step == 7) {

                expl.innerHTML = "Slightly rotate the head on the X and Y axis:<br/> " +
                    "<span class='formula'>head.rotation.x = cos (<span class='angle'>t</span>) * 0.08 <br/> " +
                    "head.rotation.y = cos (<span class='angle'>t</span>) * 0.04 </span> ";
                hero.head.material = currentMaterial;

            } else if (step == 8) {

                expl.innerHTML = "Finetune for a more natural movement:<br/>" +
                    "<span class='formula'>" +
                    "if (t>PI){<br/>" +
                    "&nbsp;&nbsp;this.legR.rotation.z = cos(<span class='angle'>t*2+PI/2</span>) * PI/4;<br/>" +
                    "&nbsp;&nbsp;this.legL.rotation.z = 0;<br/>" +
                    "} else{<br/>" +
                    "&nbsp;&nbsp;this.legR.rotation.z = 0;<br/>" +
                    "&nbsp;&nbsp;this.legL.rotation.z = cos(<span class='angle'>t*2 + PI/2</span>) *  PI/4;<br/>" +
                    "}" + "</span>";
                hero.legR.material = currentMaterial;
                hero.legL.material = currentMaterial;
            } else {
                expl.innerHTML = "Adjust the speed:<br/>" +
                    "<span class='angle'>t</span> = t * 2";
            }

        }

        // OTHER VARIABLES

        var PI = Math.PI;
        var hero;
        var clock;
        var container;
        //var gui = new dat.GUI();

        // MATERIALS

        var brownMat = new THREE.MeshStandardMaterial({
            color: 0x401A07,
            side: THREE.DoubleSide,
            shading: THREE.SmoothShading,
            roughness: 1,
        });

        var blackMat = new THREE.MeshPhongMaterial({
            color: 0x100707,
            shading: THREE.FlatShading,
        });

        var redMat = new THREE.MeshPhongMaterial({
            color: 0xAA5757,
            shading: THREE.FlatShading,
        });

        var blueMat = new THREE.MeshPhongMaterial({
            color: 0x5b9696,
            shading: THREE.FlatShading,
        });

        var whiteMat = new THREE.MeshPhongMaterial({
            color: 0xffffff,
            shading: THREE.FlatShading,
        });

        var currentMaterial = new THREE.MeshPhongMaterial({
            color: 0xff0000,
            shading: THREE.FlatShading,
        });


        //INIT THREE JS, SCREEN AND MOUSE EVENTS

        function initScreenAnd3D() {
            container = document.getElementById('world');
            HEIGHT = container.offsetHeight;
            WIDTH = container.width;
            windowHalfX = WIDTH / 2;
            windowHalfY = HEIGHT / 2;

            scene = new THREE.Scene();

            scene.fog = new THREE.Fog(0xd6eae6, 150, 300);

            aspectRatio = WIDTH / HEIGHT;
            fieldOfView = 50;
            nearPlane = 1;
            farPlane = 2000;
            camera = new THREE.PerspectiveCamera(
                fieldOfView,
                aspectRatio,
                nearPlane,
                farPlane
            );
            camera.position.x = 0;
            camera.position.z = 100;
            camera.position.y = 0;
            //camera.lookAt(new THREE.Vector3(0, 30, 0));

            renderer = new THREE.WebGLRenderer({
                alpha: true,
                antialias: true
            });
            renderer.setSize(WIDTH, HEIGHT);
            renderer.setPixelRatio(window.devicePixelRatio ? window.devicePixelRatio : 1)
            renderer.shadowMap.enabled = true;

            container.appendChild(renderer.domElement);

            window.addEventListener('resize', handleWindowResize, false);

            clock = new THREE.Clock();
            handleWindowResize();
        }

        function handleWindowResize() {
            HEIGHT = container.offsetHeight;
            WIDTH = container.offsetWidth;
            windowHalfX = WIDTH / 2;
            windowHalfY = HEIGHT / 2;
            renderer.setSize(WIDTH, HEIGHT);
            camera.aspect = WIDTH / HEIGHT;
            camera.updateProjectionMatrix();
        }

        function createLights() {
            globalLight = new THREE.AmbientLight(0xffffff, 1);
            shadowLight = new THREE.DirectionalLight(0xffffff, 1);
            shadowLight.position.set(10, 8, 8);
            shadowLight.castShadow = true;
            shadowLight.shadow.camera.left = -40;
            shadowLight.shadow.camera.right = 40;
            shadowLight.shadow.camera.top = 40;
            shadowLight.shadow.camera.bottom = -40;
            shadowLight.shadow.camera.near = 1;
            shadowLight.shadow.camera.far = 1000;
            shadowLight.shadow.mapSize.width = shadowLight.shadow.mapSize.height = 2048;
            scene.add(globalLight);
            scene.add(shadowLight);
        }

        Hero = function() {
            this.runningCycle = 0;
            this.mesh = new THREE.Group();
            this.body = new THREE.Group();
            this.mesh.add(this.body);

            var torsoGeom = new THREE.CubeGeometry(8, 8, 8, 1); //
            this.torso = new THREE.Mesh(torsoGeom, blueMat);
            this.torso.position.y = 8;
            this.torso.castShadow = true;
            this.body.add(this.torso);

            var handGeom = new THREE.CubeGeometry(3, 3, 3, 1);
            this.handR = new THREE.Mesh(handGeom, brownMat);
            this.handR.position.z = 7;
            this.handR.position.y = 8;
            this.body.add(this.handR);

            this.handL = this.handR.clone();
            this.handL.position.z = -this.handR.position.z;
            this.body.add(this.handL);

            var headGeom = new THREE.CubeGeometry(16, 16, 16, 1); //
            this.head = new THREE.Mesh(headGeom, blueMat);
            this.head.position.y = 21;
            this.head.castShadow = true;
            this.body.add(this.head);

            var legGeom = new THREE.CubeGeometry(8, 3, 5, 1);

            this.legR = new THREE.Mesh(legGeom, brownMat);
            this.legR.position.x = 0;
            this.legR.position.z = 7;
            this.legR.position.y = 0;
            this.legR.castShadow = true;
            this.body.add(this.legR);

            this.legL = this.legR.clone();
            this.legL.position.z = -this.legR.position.z;
            this.legL.castShadow = true;
            this.body.add(this.legL);

            this.body.traverse(function(object) {
                if (object instanceof THREE.Mesh) {
                    object.castShadow = true;
                    object.receiveShadow = true;
                }
            });
        }

        Hero.prototype.run = function() {
            var s = .03;
            var t = this.runningCycle;

            if (step > 8) t *= 2;
            t = t % (2 * PI);

            var amp = 4;
            var disp = .2;

            this.legR.rotation.z = 0;
            this.legR.position.y = 0;
            this.legR.position.x = 0;
            this.legL.rotation.z = 0;
            this.legL.position.y = 0;
            this.legL.position.x = 0;


            if (step > 0) {
                this.runningCycle += s;
                this.legR.position.x = Math.cos(t) * amp;
                this.legR.position.y = -Math.sin(t) * amp;
            }
            if (step > 1) {
                this.legL.position.x = Math.cos(t + PI) * amp;
                this.legL.position.y = -Math.sin(t + PI) * amp;
            }
            if (step > 2) {
                this.legL.position.y = Math.max(0, this.legL.position.y);
                this.legR.position.y = Math.max(0, this.legR.position.y);
            }
            if (step > 3) {
                this.torso.position.y = 8 - Math.cos(t * 2) * amp * .2;
                this.head.position.y = 21 - Math.cos(t * 2) * amp * .3;
            }
            if (step > 4) {
                this.torso.rotation.y = -Math.cos(t + PI) * amp * .05;
            }
            if (step > 5) {
                this.handR.position.x = -Math.cos(t) * amp;
                this.handR.rotation.z = -Math.cos(t) * PI / 8;
                this.handL.position.x = -Math.cos(t + PI) * amp;
                this.handL.rotation.z = -Math.cos(t + PI) * PI / 8;
            }
            if (step > 6) {
                this.head.rotation.x = Math.cos(t) * amp * .02;
                this.head.rotation.y = Math.cos(t) * amp * .01;
            }
            if (step > 7) {
                if (t > PI) {
                    this.legR.rotation.z = Math.cos(t * 2 + PI / 2) * PI / 4;
                    this.legL.rotation.z = 0;
                } else {
                    this.legR.rotation.z = 0;
                    this.legL.rotation.z = Math.cos(t * 2 + PI / 2) * PI / 4;
                }
            }
        }

        function createHero() {
            hero = new Hero();
            hero.mesh.position.y = -15;
            scene.add(hero.mesh);
        }
        var rot = -1;

        function loop() {

            updateTrigoCircle(hero.runningCycle);
            hero.run();
            rot += .01;
            hero.mesh.rotation.y = -Math.PI / 4 + Math.sin(rot * Math.PI / 8);
            render();
            requestAnimationFrame(loop);
        }

        function render() {
            renderer.render(scene, camera);
        }

        window.addEventListener('load', init, false);

        function init(event) {
            initScreenAnd3D();
            createLights();
            createHero();
            loop();
            initNav();
        }


        // Trigo Circle

        var PI = Math.PI;
        var trigoArc = document.getElementById("trigoArc");
        var trigoLine = document.getElementById("trigoLine");
        var trigoPoint = document.getElementById("trigoPoint");
        var cosPoint = document.getElementById("cosPoint");
        var sinPoint = document.getElementById("sinPoint");
        var cosLine = document.getElementById("cosLine");
        var sinLine = document.getElementById("sinLine");
        var projSinLine = document.getElementById("projSinLine");
        var projCosLine = document.getElementById("projCosLine");

        var cAngle = 0;
        var tp = {
            radiusArc: 10,
            centerX: 60,
            centerY: 60,
            radiusLines: 50,
        };

        function updateTrigoCircle(angle) {
            angle %= PI * 2;
            var cos = Math.cos(angle);
            var sin = Math.sin(angle);
            var start = {
                x: tp.centerX + tp.radiusArc * cos,
                y: tp.centerY + tp.radiusArc * sin
            };
            var end = {
                x: tp.centerX + tp.radiusArc,
                y: tp.centerY
            };

            var arcSweep = angle >= PI ? 1 : 0;
            var d = ["M", tp.centerX, tp.centerY,
                "L", start.x, start.y,
                "A", tp.radiusArc, tp.radiusArc, 0, arcSweep, 0, end.x, end.y,
                "L", tp.centerX, tp.centerY
            ].join(" ");

            trigoArc.setAttribute("d", d);
            trigoLine.setAttribute("x2", tp.centerX + cos * tp.radiusLines);
            trigoLine.setAttribute("y2", tp.centerY + sin * tp.radiusLines);
            trigoPoint.setAttribute("cx", tp.centerX + cos * tp.radiusLines);
            trigoPoint.setAttribute("cy", tp.centerY + sin * tp.radiusLines);
            cosPoint.setAttribute("cx", tp.centerX + cos * tp.radiusLines);
            sinPoint.setAttribute("cy", tp.centerY + sin * tp.radiusLines);
            cosLine.setAttribute("x2", tp.centerX + cos * tp.radiusLines);
            sinLine.setAttribute("y2", tp.centerY + sin * tp.radiusLines);
            projSinLine.setAttribute("x2", tp.centerX + cos * tp.radiusLines);
            projSinLine.setAttribute("y2", tp.centerY + sin * tp.radiusLines);
            projSinLine.setAttribute("y1", tp.centerY + sin * tp.radiusLines);
            projCosLine.setAttribute("x2", tp.centerX + cos * tp.radiusLines);
            projCosLine.setAttribute("x1", tp.centerX + cos * tp.radiusLines);
            projCosLine.setAttribute("y2", tp.centerY + sin * tp.radiusLines);
        }
    </script>
</body>

</html>